"""
Schoolloop backend

This file is part of polyglot, etc.
"""

import datetime
import requests
import re
import hashlib
import json
from lxml.html import parse
from io import StringIO
from .support import Backend, Course, AssignmentGrade, CategoryGrade, Task, ResourcePlain, ResourceLink, Inbox, Message, Thread

class SchoolloopMessage(Message):
    def __init__(self, backend, url, date, sender, recipient, subject):
        self.date = date
        self.sender = sender
        self.recipient = recipient
        self.subject = subject
        self.backend = backend
        self.url = url

    @property
    def body(self):
        s = self.backend.session.get(self.backend.base_url + self.url)
        tree = parse(StringIO(s.text)).getroot()

        return tree.cssselect(".mail_message")[0].text_content().strip()


class SchoolloopInbox(Inbox):
    def __init__(self, backend):
        self.backend = backend

    @property
    def threads(self):
        # Fetch the latest 15 messages (first page) from the direct AJAX loopmail
        # There is no API, but this is still better (lower bandwidth) than fetching the whole page

        s = self.backend.session.get(self.backend.base_url + "/loopmail/inbox?d=x&form_filter=&template=ajax_form_filter&source=form_filter&active=true&form_rows=")
        tree = parse(StringIO(s.text)).getroot()

        threads = []

        for thread in tree.cssselect(".standard_body")[0].cssselect("tr"):
            tds = thread.cssselect("td")
            (_, date, author, subject) = [td.text_content().strip() for td in tds]
            url = thread.cssselect("a")[0].attrib["href"]
            thread_id = re.search("id=([^&]+)", url).group(1)

            threads += [Thread(thread_id, [SchoolloopMessage(self.backend, url, date, author, "You", subject)])]

        return threads

    def send(self, to, subject, body):
        # We don't really need the form, but we need to snarf out the ID

        s = self.backend.session.get(self.backend.base_url + "/loopmail/send")
        form_id = re.search('var formDataID = "(\d+)"', s.text).group(1)

        # In truth, this is a real-time API, but polyglot does not expose bindings for that,
        # and neither the CLI nor Palette can make good use of that
        # So, we require a correctly specified full name, and just cheat our way around it a bit...

        last_name = to.split(" ")[-1] # This does not internationalise well, which is bad for schools like mine...

        r = self.backend.session.get(self.backend.base_url + "/loopmail/users", params = {
            "registered": "true",
            "q": last_name
        })
        
        res = json.loads(r.text)

        best_score = 0
        best_candidate = None

        for result in res:
            candidate_parts = (result["name"] + " " + result["role"].strip()).upper().split(" ")
            requested_parts = to.upper().split(" ")
            score = len(set(candidate_parts) & set(requested_parts))

            if score > best_score:
                best_candidate = result
                best_score = score

        if best_candidate is None:
            print("No candidate found")
            return False
        
        # I'm still not totally comfortable with this algorithm...
        print(best_candidate)

        to_id = best_candidate["id"]

        data = {
                "email_address_to": to_id,
                "email_address_cc": "",
                "subject": subject,
                "message": body,
                "form_reverse": "",
                "hidden_address_cc=": "",
                "show_selected=": "",
                "form_sort": "",
                "hidden_address_bcc": "",
                "forward": "",
                "form_page_item_count": "",
                "form_data_id": form_id,
                "sort": "",
                "reverse": "",
                "form_letter": "",
                "event_override": "send",
                "permissions": "1",
                "return_url": "",
                "hidden_address_to": to_id,
                "form_filter": "",
                "form_page_index": "",
                "id": "",
                "folder_id": ""
        }

        self.backend.session.post(self.backend.base_url + "/loopmail/send?etarget=form", data=data)

class SchoolloopTask(Task):
    def __init__(self, backend, name, due, url):
        super().__init__(name, due)
        self.backend = backend
        self.url = url

    @property
    def resources(self):
        s = self.backend.session.get(self.backend.base_url + self.url, headers = {
            "X-Requested-With": "XMLHttpRequest",
            "Referer": self.backend.base_url + "/portal/student_home"
        })

        tree = parse(StringIO(s.text)).getroot()

        rsrcs = []

        for body in tree.cssselect(".sllms-content-body"):
            rsrcs += [ResourcePlain("Body", body.text_content().strip())]

        for attachment in tree.cssselect(".attached-item a"):
            rsrcs += [ResourceLink(attachment.text_content().strip(), self.backend.base_url + attachment.attrib["href"])]

        return rsrcs

class SchoolloopCourse(Course):
    def __init__(self, backend, row):
        self.row = row
        self.backend = backend

        self.period = row.cssselect(".period")[0].text_content().strip()
        self.title = row.cssselect(".course")[0].text_content().strip()
        self.teacher = row.cssselect(".teacher")[0].text_content().strip()

        if len(row.cssselect(".percent")):
            self.grade_summary = float(row.cssselect(".percent")[0].text_content().strip()[:-1]) / 100.0
        else:
            self.grade_summary = None

        self.id = hashlib.md5((self.period + self.title + self.teacher).encode('utf-8')).hexdigest()

    @property
    def grades(self):
        href = self.row.cssselect(".pr_link > a")[0].attrib["href"]
        report = self.backend.session.get(self.backend.base_url + href)

        score_extractor = re.compile("([0-9\.]+) / ([0-9\.]+)")

        pr_root = parse(StringIO(report.text)).getroot()

        categories = {}

        for row in pr_root.cssselect('.module:contains("Scores per Category")')[0].cssselect("tr")[1:]:
            r = row.getchildren()
            category = r[0].text_content().strip()
            weight = r[1].text_content().strip()

            categories[category] = CategoryGrade(category, float(weight[0:-1]) / 100.0, [])

        for row in pr_root.cssselect(".general_body")[0].getchildren():
            r = row.getchildren()
            category = r[0].getchildren()[0].text.strip()
            assignment = r[0].cssselect("a")[0].text_content().strip()
            due = r[2].text_content().strip()

            ex = score_extractor.search(r[3].text_content().strip())
            (score_numerator, score_denominator) = ex.groups() if ex else (None, None)

            comment = r[4].text_content().strip()

            if category in categories:
                categories[category].grades += [AssignmentGrade(assignment, float(score_numerator) if score_numerator else None, float(score_denominator) if score_denominator else None, comment)]

        return list(categories.values())

    @property
    def tasks(self):
        try:
            return self.backend.tasks_by_course[self.title]
        except KeyError:
            return []

class SchoolloopBackend(Backend):
    Config = ["Username", "Password", "School"]

    def login(self, config):
        # First, fetch the login portal itself
        self.base_url = "https://" + config["School"] + ".schoolloop.com"

        form = self.session.get(self.base_url + "/portal/login")

        # Extract data id
        data_extractor = re.compile('<input type="hidden" name="form_data_id" id="form_data_id" value="(\\d+)">')
        data_id = data_extractor.search(form.text).group(1)

        credentials = {
            "login_name": config["Username"],
            "password": config["Password"],
            "redirect": "",
            "forward": "",
            "login_form_reverse": "",
            "form_data_id": str(data_id),
            "sort": "",
            "reverse": "",
            "login_form_sort": "",
            "event_override": "login",
            "login_form_filter": "",
            "login_form_letter": "",
            "return_url": "",
            "login_form_page_index": "",
            "login_form_page_item_count": ""
        }

        portal = self.session.post(self.base_url + "/portal/login?etarget=login_form", data=credentials)

        # Save session
        self.portal = portal.text
        self.root_element = parse(StringIO(self.portal)).getroot()

        return True

    def fast_login(self, config):
        self.base_url = "https://" + config["School"] + ".schoolloop.com"
        self.portal = self.session.get(self.base_url + "/portal/student_home").text
        self.root_element = parse(StringIO(self.portal)).getroot()

    @property
    def courses(self):
        self.fetch_tasks()

        rows = self.root_element.cssselect(".student_row")
        ret = []

        for row in rows:
            ret += [SchoolloopCourse(self, row)]

        return ret

    def fetch_tasks(self):
        task_boxes = self.root_element.cssselect('[data-track-container="Active Assignments"]')[0].cssselect('.ajax_accordion_row')
        self.tasks_by_course = {}

        for box in task_boxes:
            rows = box.cssselect("tr")[0]
            r = rows.getchildren()
            title = r[3].text_content().strip()
            course = r[4].text_content().strip()
            due = r[5].text_content().replace("Due:", "").strip() # Help! Internationalisation >_<

            if course not in self.tasks_by_course:
                self.tasks_by_course[course] = []

            self.tasks_by_course[course] += [SchoolloopTask(self, title, due, box.attrib["data-url"])]

    @property
    def inbox(self):
        return SchoolloopInbox(self)
